package com.googlecode.perfectable.visuable.visual;

import java.util.List;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.googlecode.perfectable.visuable.renderer.ElementRenderer;
import com.googlecode.perfectable.visuable.renderer.VisualRenderer;
import com.googlecode.perfectable.visuable.structure.Attribute;

public class StandardVisual implements ParentVisual, TextualVisual {

	private final String elementName;
	private final List<Attribute> attributes;
	private final List<Visual> contents;
	private String innerText;
	private String identifier;

	StandardVisual(String elementName, List<Attribute> elementAttributes, String innerText, List<Visual> children) {
		this.elementName = elementName;
		this.attributes = Lists.newArrayList(elementAttributes);
		this.identifier = IdentifiedVisuals.extractIdentifier(this.attributes);
		this.innerText = innerText;
		this.contents = Lists.newLinkedList(children);
	}

	@Override
	public String identifier() {
		return this.identifier;
	}

	@Override
	public final void render(VisualRenderer painter) {
		try(ElementRenderer nestedPainter = painter.element(this.elementName)) {
			this.renderElement(nestedPainter);
		}
	}

	protected final void renderElement(ElementRenderer nestedPainter) {
		this.renderAttributes(nestedPainter);
		this.renderText(nestedPainter);
		try(VisualRenderer visualPainter = nestedPainter.children()) {
			this.renderContents(visualPainter);
		}
	}

	protected void renderAttributes(ElementRenderer nestedPainter) {
		IdentifiedVisuals.render(nestedPainter, this);
		for(Attribute attribute : this.attributes) {
			attribute.render(nestedPainter);
		}
	}

	protected void renderText(ElementRenderer nestedPainter) {
		nestedPainter.raw(this.innerText);
	}

	protected void renderContents(VisualRenderer visualPainter) {
		for(Visual content : this.contents) {
			content.render(visualPainter);
		}
	}

	@Override
	public void append(Visual visual) {
		this.contents.add(visual);
	}

	@Override
	public String text() {
		return this.innerText;
	}

	@Override
	public void setText(String text) {
		this.innerText = text;
	}

	@Override
	public void appendText(String text) {
		this.innerText += text;
	}

	public static ElementVisualBuilder builder(String elementName) {
		return new ElementVisualBuilder(elementName);
	}

	public static final class ElementVisualBuilder { // MARK interface
		private final String elementName;
		private final ImmutableList.Builder<Attribute> attributesBuilder = new ImmutableList.Builder<>();
		private final ImmutableList.Builder<Visual> childrenBuilder = new ImmutableList.Builder<>();
		private final StringBuilder textBuilder = new StringBuilder();

		public ElementVisualBuilder(String elementName) {
			this.elementName = elementName;
		}

		public ElementVisualBuilder withAttributes(Iterable<Attribute> attributes) {
			this.attributesBuilder.addAll(attributes);
			return this;
		}

		public void withChild(Visual child) {
			this.childrenBuilder.add(child);
		}

		public ElementVisualBuilder withTextPart(String text) {
			this.textBuilder.append(text);
			return this;
		}

		public StandardVisual build() {
			List<Attribute> attributes = this.attributesBuilder.build();
			String text = this.textBuilder.toString();
			List<Visual> children = this.childrenBuilder.build();
			final StandardVisual result = new StandardVisual(this.elementName, attributes, text, children);
			return result;
		}

	}

}
